﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace XmlServer.PoolServicer
{
    /// <summary>
    /// The PoolServicer object manages the ClientPool.  It is responsible for adding and fetching/removing
    /// ClientHandler objects from the pool
    /// </summary>
    public class PoolServicer
    {

        #region Local Variables

        // The number of threads to use to process the client requests in the pool
        private int numThreads = 20;

        // The number of threads that are currently working
        private int workingThreads = 0;

        // The pool/queue of client requests
        private ClientPool clientPool = null;

        // This module level variable will case this class to stop processing the
        // client requests that have accumulated in the pool
        private bool continueProcess = false;

        // the thread that do the processing
        private Thread[] processThreads = null;

        // PoolServicer has a reference to the listener
        Listeners.Listener listener = null;

        // data returned from the status requests
        private ulong clientsProcessed = 0;
        private ulong totalClientPoolTime = 0;

        #endregion

        #region Public Constructor

        public PoolServicer(int NumThreads, ClientPool clientPool, Listeners.Listener listener)
        {
            this.numThreads = NumThreads;
            this.clientPool = clientPool;
            this.listener = listener;
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// The number of threads used to manage the pool
        /// </summary>
        public int NumThreads 
        {
            get { return this.numThreads;  }
            set { this.numThreads = value; } 
        }


        /// <summary>
        /// The number of clients processed by this pool servicer
        /// </summary>
        public ulong ClientsProcessed
        {
            get { return this.clientsProcessed; }
            set { this.clientsProcessed = value; }
        }


        /// <summary>
        /// The total amount of time clients have spent in this pool manager
        /// </summary>
        public ulong TotalClientPoolTime
        {
            get { return this.totalClientPoolTime; }
            set { this.totalClientPoolTime = value; }
        }


        /// <summary>
        /// The average amount of time a client is in this pool servicer
        /// </summary>
        public double AverageClientPoolTime 
        { 
            get
            {
                double temp = 0;
                try
                {
                    var clients = ClientsProcessed;
                    if (clients > 0)
                        temp = TotalClientPoolTime / clients;
                }
                catch
                {
                    temp = 0;
                }
                return temp;
            }
        }


        /// <summary>
        /// The number of clients in the pool
        /// </summary>
        public int ClientPoolCount 
        { 
            get
            {
                return clientPool.Count;
            }
        }


        /// <summary>
        /// The number of processor threads that are currently servicing client requests
        /// </summary>
        public int WorkingThreads
        {
            get
            {
                int workThreadCount = 0;
                try
                {
                    Monitor.Enter(this);
                    workThreadCount = workingThreads;
                }
                catch { }
                finally
                {
                    Monitor.Exit(this);
                }
                return workThreadCount;
            }
        }


        /// <summary>
        /// The estimated amount of time required to finish processing all outstanding requests
        /// </summary>
        public double EstimatedCleanupTime
        {
            get
            {
                double temp = 0;
                try
                {
                    temp = AverageClientPoolTime * ClientPoolCount;
                }
                catch
                {
                    temp = 0;
                }
                return temp;
            }
        }



        #endregion

        #region Public Methods

        /// <summary>
        /// Start the pool servicer
        /// </summary>
        public void Start()
        {
            // class scope variable - allow the servier thread to continue working
            continueProcess = true;

            // create the worker thread array
            processThreads = new Thread[numThreads];
            for (int i=0; i<numThreads;i++)
            {
                processThreads[i] = new Thread(new ThreadStart(Process));
                processThreads[i].Start();
            }

        }


        /// <summary>
        /// Stop the pool servicing
        /// </summary>
        public void Stop()
        {
            // stop the processing threads from dequeuing ClientHandlers from the pool/queue
            continueProcess = false;

            // sleep to allow the current worker threads to finish their worl
            Thread.Sleep(numThreads * 100);

            // loop through each thread and stop them
            for (int i=0; i<numThreads; i++)
            {
                try
                {
                    if (processThreads[i] != null)
                    {
                        if (processThreads[i].IsAlive == true)
                        {
                            processThreads[i].Abort();

                            /*
                            processThreads[i].Join(1000);
                            if (processThreads[i].IsAlive == true) processThreads[i].Abort();
                            */
                        }
                    }
                }
                catch
                {
                    // do nothing
                }
            }

            // clear the clients from the pool
            clientPool.Clear();
        }

        #endregion


        /// <summary>
        /// The processor threads will call this method to handle the client requests
        /// </summary>
        private void Process()
        {
            // the processor threads will continue to process while this variable is true
            while (continueProcess)
            {
                // get a client handler to be processed
                ClientHandler client = null;
                try
                {
                    // get the next ClientHandler to be processed from the pool/queue.
                    // IMPORTANT: This will remove the ClientHandler from the queue/pool
                    client = clientPool.NextClient();
                }
                catch
                {
                    client = null;
                }

                // see if we got a client to process
                if (client != null)
                {
                    IncrementWorkingThreads();
                    try
                    {
                        client.Process();
                    }
                    catch { }

                    // if the client is still connected, schedule it for later processing (by adding it back into the pool/queue)
                    // if the client is not alive anymore, we are done processing it....DO NOT ADD BACK TO POOL/QUEUE
                    if (client.Alive)
                    {
                        // the client still needs to be processed; add it back to the pool/queue
                        clientPool.Add(client);
                    }
                    else
                    {
                        try
                        {
                            // the client finished processing
                            IncrementClientsProcessed();
                            IncrementTotalClientPoolTime(client.ProcessingTime);
                        }
                        catch { }
                    }

                    DecrementWorkingThreads();
                }

                // sleep the thread
                Thread.Sleep(100);
            }


        }

        /// <summary>
        /// Increment the working thread count
        /// </summary>
        private void IncrementWorkingThreads()
        {
            try
            {
                Monitor.Enter(this);
                workingThreads++;
            }
            catch { }
            finally
            {
                Monitor.Exit(this);
            }
        }

        private void DecrementWorkingThreads()
        {
            try
            {
                Monitor.Enter(this);
                workingThreads--;
            }
            catch { }
            finally
            {
                Monitor.Exit(this);
            }
        }

        private void IncrementClientsProcessed()
        {
            try
            {
                Monitor.Enter(this);
                clientsProcessed++;
            }
            catch { }
            finally
            {
                Monitor.Exit(this);
            }
        }


        private void IncrementTotalClientPoolTime(int ClientTime)
        {
            try
            {
                Monitor.Enter(this);
                this.totalClientPoolTime += Convert.ToUInt64(ClientTime);
            }
            catch { }
            finally
            {
                Monitor.Exit(this);
            }
        }


    }
}
